﻿// =========================================================================================================
// FAS低代码开发平台，SQL Server目标库的SQLBuilder类
// 说明：
//    设计时发布DO，同时在目标环境创建物理表或同步数据表变更功能开发。
// 主要功能：
// 1、支持根据DO元数据生成Create Table的DDL语句
// 2、支持根据DO元数据变更比较生成Alter Table的DDL语句
// 3、支持根据SDO元数据中预置数据Content，生成Insert预置数据的DML语句
// =========================================================================================================
// 变更历史纪录
// 日期			版本	    修改人	描述
// 2021.8.24    1.0		user2	初稿。
// =========================================================================================================
using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.DataModel.Dao;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using Const = T.FAS.Runtime.DataModel.Plugin.MetaManagerConst;
using DataColumn = T.FAS.Runtime.Metadata.Entity.DataModel.DataColumn;
//using MySql.Data.MySqlClient;
using NPOI.SS.Formula.Eval;
using T.FAS.Runtime.Base.Common;

namespace T.FAS.Runtime.DataModel.Plugin
{
    /// <summary>
    /// DataObject MSSQL Builder
    /// </summary>
    public class DataObjectMSSQLBuilder : DataObjectSQLBuilderBase, IMetaBusinessSQLBuilder
    {
        protected override DatabaseType DatabaseType => DatabaseType.SQLServer;
        private IList<DataObject> contents = new List<DataObject>();

        /// <summary>
        /// 同批次发布的DO元数据集合，方便DO查询外键类型的列的数据类型
        /// </summary>
        public override IList<IMetaDataContent> Contents
        {
            get
            {
                return contents.Select(o => (IMetaDataContent)o).ToList();
            }
            set
            {
                contents?.Clear();
                contents = value.Select(o => (DataObject)o).ToList();
            }
        }


        #region Private Funcs
        /// <summary>
        /// 生成Drop Column SQL
        /// </summary>
        /// <param name="dataObject">DO实体</param>
        /// <param name="columns">DO列实体集合</param>
        /// <returns>可执行的SQL集合</returns>
        private List<SQLItem> GetColumnDropSQL(DataObject dataObject, List<DataColumn> columns)
        {
            var template = GetTemplate(dataObject);
            //删除column SQL Item
            List<SQLItem> dropColumns = new List<SQLItem>();
            foreach (var column in columns)
            {
                SQLItem item = template.Clone();
                //MSSQL需要先删除约束
                item.SQL += Const.MSSQL.DefaultDrop.Format(
                        new Dictionary<string, object>
                        {
                            {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                            {Const.column_name, ProcessCaseString(column.ColumnName) },
                        });
                var dropColSQL = Const.MSSQL.ColumnDrop.Format(
                    new Dictionary<string, object>
                    {
                        {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                        {Const.column_name, ProcessCaseString(column.ColumnName) },
                    });
                item.SQL += dropColSQL;
                dropColumns.Add(item);
            }
            return dropColumns;
        }

        /// <summary>
        /// 生成Modify Column SQL
        /// </summary>
        /// <param name="dataObject">DO实体</param>
        /// <param name="columns">DO列实体集合</param>
        /// <returns>可执行的SQL集合</returns>
        protected override List<SQLItem> GetColumnModifySQL(DataObject dataObject, List<ModifyColumnInfo> columns)
        {
            //修改column SQL Item
            var template = GetTemplate(dataObject);
            List<SQLItem> modifyColumnsSQLs = new List<SQLItem>();
            foreach (var modifyColInfo in columns)
            {
                SQLItem modifyColItem = template.Clone();

                string databaseDataType = DataTypeTool.GetMappingDataType(template.DatabaseType, modifyColInfo.NewColumn, contents);
                //非只包含国际化变更时，生成modify语句
                if (modifyColInfo.ChangeTypeLayer != (int)ChangeType.IsInternational)
                {
                    //如果包含默认值，需要删除默认值约束
                    bool defaultChangedFlag = (modifyColInfo.ChangeTypeLayer & (int)ChangeType.DefaultValue) == (int)ChangeType.DefaultValue;
                    bool isHaveDefaultVal = !string.IsNullOrEmpty(modifyColInfo.NewColumn.DefaultValue);
                    //如果包含默认值，或者默认值为0并且默认值发生了变动时，需要删除默认值
                    if (isHaveDefaultVal || (defaultChangedFlag && string.IsNullOrEmpty(modifyColInfo.NewColumn.DefaultValue)))
                    {
                        modifyColItem.SQL += Const.MSSQL.DefaultDrop.Format(
                            new Dictionary<string, object>
                            {
                                {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                                {Const.column_name, ProcessCaseString(modifyColInfo.NewColumn.ColumnName) },
                            });
                    }

                    var modifyColSQL = Const.MSSQL.ColumnModify.Format(
                        new Dictionary<string, object>
                        {
                            { Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                            { Const.column_name, ProcessCaseString(modifyColInfo.NewColumn.ColumnName) },
                            { Const.data_type, databaseDataType},
                            { Const.nullable, modifyColInfo.NewColumn.IsNullable ? " NULL ": " NOT NULL " },
                            //{Const.@default, DealWithDefault(modifyColInfo.NewColumn) }
                        });

                    //如果为国际化列，则同步生成modify变更DDL
                    if (modifyColInfo.NewColumn.IsInternational)
                    {
                        var columnAddTemplate = Const.MSSQL.ColumnModify;

                        bool isStringType = string.Equals(modifyColInfo.NewColumn.ApplicationDataType, DataType.String.ToString(), StringComparison.OrdinalIgnoreCase);
                        if (isStringType)
                        {
                            foreach (var langKey in InternationalDataTools.DefaultParalanguage)
                            {
                                SQLItem tmp = template.Clone();
                                var i18NColumnName = DataColumn.GetInternationalColumnName(dataObject, modifyColInfo.NewColumn.ColumnName, langKey);
                                var internationalColumnSQL = columnAddTemplate.Format(
                                    new Dictionary<string, object>
                                    {
                                        {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                                        {Const.column_name, ProcessCaseString(i18NColumnName) },
                                        {Const.data_type, databaseDataType},
                                        {Const.nullable, " NULL "}, //国际化列必须可空
                                        {Const.@default, DealWithDefault(modifyColInfo.NewColumn) },
                                        {Const.comment, DealWithComment(modifyColInfo.NewColumn, true) },
                                    });
                                tmp.SQL = internationalColumnSQL;
                                modifyColumnsSQLs.Add(tmp);
                            }
                        }
                    }

                    //修改SQL Server的主键列时，需要先删除主键约束，然后修改列信息，最后重新添加主键约束
                    if (modifyColInfo.NewColumn.IsPkColumn)
                    {
                        //删除主键约束
                        string dropPkCons = Const.MSSQL.DropPKConstraint.Format(new Dictionary<string, object>
                        {
                            {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                        });
                        //添加主键约束
                        string addPkCons = Const.MSSQL.PKAdd.Format(new Dictionary<string, object>
                        {
                            {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                            {Const.pk_column_name, ProcessCaseString(modifyColInfo.NewColumn.ColumnName) },
                        });
                        modifyColSQL = $"{dropPkCons}\r\n\r\n{modifyColSQL}\r\n\r\n{addPkCons}\r\n\r\n";
                    }

                    //如果默认值发生改动
                    //bool defaultChangedFlag = (modifyColInfo.ChangeTypeLayer & (int)ChangeType.DefaultValue) == (int)ChangeType.DefaultValue;
                    if (isHaveDefaultVal)
                    {
                        //重新添加默认值约束
                        modifyColSQL += Const.MSSQL.DefaultAdd.Format(
                            new Dictionary<string, object>
                            {
                                {Const.table_name, ProcessCaseString(dataObject.LogicTableName) },
                                {Const.column_name, ProcessCaseString(modifyColInfo.NewColumn.ColumnName) },
                                {Const.@default, DealWithDefault(modifyColInfo.NewColumn) }
                            });
                    }

                    modifyColItem.SQL += modifyColSQL;
                    modifyColumnsSQLs.Add(modifyColItem);
                }
                //当列设置为国际化时，需要额外生成Add Column 语句
                bool isInternationalChanged = (modifyColInfo.ChangeTypeLayer & (int)ChangeType.IsInternational) == (int)ChangeType.IsInternational;
                if (isInternationalChanged && modifyColInfo.NewColumn.IsInternational)
                {
                    var internationalSQLs = GetInternalColumnAddSQLs(dataObject, databaseDataType, modifyColInfo.NewColumn);
                    if (internationalSQLs != null && internationalSQLs.Count > 0)
                    {
                        foreach (var internationalSQL in internationalSQLs)
                        {
                            SQLItem internationalSQLItem = template.Clone();
                            internationalSQLItem.SQL = internationalSQL;
                            modifyColumnsSQLs.Add(internationalSQLItem);
                        }
                    }
                }

                //当列设置为掩码时，需要额外生成Add Column 语句
                bool isMaskingChanged = (modifyColInfo.ChangeTypeLayer & (int)ChangeType.MaskSetting) == (int)ChangeType.MaskSetting;
                if (isMaskingChanged && modifyColInfo.NewColumn.MaskingRule.IsEnable && ConfigUtil.Instance.EnableFASMaskStorage)
                {
                    var maskingSQLs = GetMaskingColumnAddSQLs(dataObject, databaseDataType, modifyColInfo.NewColumn);
                    if (maskingSQLs != null && maskingSQLs.Count > 0)
                    {
                        foreach (var maskingSQL in maskingSQLs)
                        {
                            SQLItem MaskingSQLItem = template.Clone();
                            MaskingSQLItem.SQL = maskingSQL;
                            modifyColumnsSQLs.Add(MaskingSQLItem);
                        }
                    }
                }
            }
            return modifyColumnsSQLs;
        }

        /// <summary>
        /// 处理注释,SQLServer不需要处理
        /// </summary>
        /// <param name="column"></param>
        /// <param name="isEscape">是否需要转义，在存储过程中使用需要进行转义</param>
        /// <returns></returns>
        protected override string DealWithComment(DataColumn column, bool isEscape = false)
        {
            return "";
        }
        #endregion
    }
}
